#pragma once

#include "../../Utils/PlatTypeUtil.h"
#include <string>
#include <vector>
#include <entt/entt.hpp>
#include "Camera.h"
#include "../Process/RenderProcess.h"

class Entity;
struct IdComponent;
struct NameComponent;

class GameScene
{
	friend class GameState;
	friend class RenderProcess;

public:

	GameScene();

	template<typename T>
	T* CreateEntity();

	template<typename T>
	T* CreateEntity(const std::string& name);

	//template <typename... Components>
	//std::vector<Entity*> GetEntitysByTypes();

	template <typename T, typename... Components>
	std::vector<T*> GetEntitysByTypes();

	template <typename T, typename Component>
	std::vector<T*> GetEntitysByType();

	template <typename R, typename T>
	void AddDependency();

	Entity* GetEntityById(uint64 id);

	Entity* GetEntityByName(const std::string& name);

	Camera& GetCamera();

	entt::registry& GetRegistry();

	void ClearEntitys();

	template<typename T>
	T* GetProcess(const std::string& name) const;

	//#########################
	// Run on Render Thread
	//#########################

	virtual void OnResize();

	virtual void OnFrameEnd();

	virtual void OnImgui();

	//#########################
	// Run on Render Thread
	//#########################

private:

	void Init();

	void Exit();

	void Tick(float deltaTime);

protected:

	virtual void OnInit() {};

	virtual void OnExit() {};

	virtual void OnTick(float deltaTime) {};

protected:

	entt::registry mRegistry;

	std::unordered_map<uint64, Entity*> mEntitys;

	Camera mCamera;

};

template<typename T>
T* GameScene::CreateEntity()
{
	entt::entity handle = mRegistry.create();
	IdComponent idComponent = mRegistry.emplace<IdComponent>(handle);
	T* entity = NewObject<T>(handle, this);
	mEntitys.insert({ idComponent.id, entity });
	return entity;
}

template<typename T>
T* GameScene::CreateEntity(const std::string& name)
{
	entt::entity handle = mRegistry.create();
	mRegistry.emplace<NameComponent>(handle, name);
	IdComponent idComponent = mRegistry.emplace<IdComponent>(handle);
	T* entity = NewObject<T>(handle, this);
	mEntitys.insert({ idComponent.id, entity });
	return entity;
}

template <typename T, typename... Components>
std::vector<T*>
GameScene::GetEntitysByTypes()
{
	std::vector<T*> result;
	// entt::group<Components...> group = mRegistry.group<Components...>();
	auto group = mRegistry.group<Components...>();
	for (uint32 i = 0; i < group.size(); ++i)
	{
		uint64 id = mRegistry.get<IdComponent>(group[i]).id;
		auto entityIter = mEntitys.find(id);
		assert(entityIter != mEntitys.end());
		result.push_back(static_cast<T*>(entityIter->second));
	}
	return result;
}

template <typename T, typename Component>
std::vector<T*>
GameScene::GetEntitysByType()
{
	std::vector<T*> result;
	auto view = mRegistry.view<Component>();
	for (uint32 i = 0; i < view.size(); ++i)
	{
		uint64 id = mRegistry.get<IdComponent>(view[i]).id;
		auto entityIter = mEntitys.find(id);
		assert(entityIter != mEntitys.end());
		result.push_back(static_cast<T*>(entityIter->second));
	}
	return result;
}

template <typename R, typename T>
void GameScene::AddDependency()
{
	mRegistry.template on_construct<R>().template connect<&entt::registry::get_or_emplace<T>>();
}

template<typename T>
T* GameScene::GetProcess(const std::string& name) const
{
	for (uint32 i = 0; i < GRenderState->mRenderProcessMap.size(); ++i)
	{
		if (GRenderState->mRenderProcessMap[i]->GetName() == name)
		{
			return static_cast<T*>(GRenderState->mRenderProcessMap[i]);
		}
	}
	assert(0);
	return nullptr;
}
